/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package master;

/**
 *
 * @author Luoaaang
 */

import java.util.ArrayList;
import java.util.PriorityQueue;

import javax.swing.JFrame;

import common.JobObject;
import common.JobsPackage;
import common.MasterObject;
import common.WorkerObject;

/**
 *
 * @author Luoaaang
 */
public class Master {
	
	// the service provided by Master
	MasterService clientService;
	// the master object
	static MasterObject master = new MasterObject();
	// the list of available Workers
	static ArrayList<WorkerObject> workerList = new ArrayList<WorkerObject>();
	// the list of jobs
	static ArrayList<JobObject> jobList = new ArrayList<JobObject>();
	// the list of job results
	static ArrayList<String> resultList = new ArrayList<String>();
	// the priority queue for jobs
	static PriorityQueue<JobObject> jobQueue = new PriorityQueue<JobObject>(10);
	// the priority queue for jobs
	static PriorityQueue<JobsPackage> jobsPackageQueue = new PriorityQueue<JobsPackage>(10, new SortByPriority("ACS"));

	// Main Program
	public static void main(String[] args) {

		// int serverIP = 127.0.0.1;
		// int serverTCPPort = 5001;

		/******************** without Rigistry mode ********************/
/*		// argument checking
		if (args.length!=2){
			System.out.println("Usage: java Master <Registry IP> <Registry TCP Port>");
			return;
		}
		
		String registryIPStr = args[0];
		InetAddress registryIP = null;
		try {
			registryIP = InetAddress.getByName(registryIPStr);
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		int registryTCPPort = Integer.parseInt(args[1]);

		System.out.println("SYSINFO - Master has been started up successfully.");
		System.out.println("SYSINFO - Master list request service start.");
		
		// initialize client services
		MasterService masterService = new MasterService(registryIP, registryTCPPort, workerList, resultList);
		
		// hardcoded a initialized job list for testing
//		jobList.add(new JobObject("masterJobs/", "HelloWorld", "Melbourne", 1000, 0));
//		jobList.add(new JobObject("masterJobs/", "GoodDay", "Syndey", 2000, 1));
		
		JobsPackage jp = new JobsPackage();
		jp.addJob(new JobObject("masterJobs/","TestQueue", "Queue1", 3000, 3));
		jp.addJob(new JobObject("masterJobs/","TestQueue", "Queue2", 4000, 4));
		jp.addJob(new JobObject("masterJobs/","TestQueue", "Queue3", 5000, 5));
		
		RequestThread requestThread = new RequestThread(masterService);
		requestThread.start();*/
		/******************** without Rigistry mode ********************/
		
		/******************** with Rigistry mode ********************/
		// argument checking
		if (args.length!=2){
			System.out.println("Usage: java Master <Master UDP Port> <Master TCP Port>");
			return;
		}
		
		int masterUDPPort = Integer.parseInt(args[0]);
		int masterTCPPort = Integer.parseInt(args[1]);
		
		// registry object
		master.setMasterUDPPort(masterUDPPort);
		master.setMasterTCPPort(masterTCPPort);
		
		System.out.println("DEBUG - Registry UDP Port: "+master.getMasterUDPPort());
		System.out.println("DEBUG - Registry TCP Port: "+master.getMasterTCPPort());
		
		System.out.println("SYSINFO - Master has been started up successfully.");
		System.out.println("SYSINFO - Master list request service start.");

		// initialize client services
		MasterService masterService = new MasterService(master, workerList, jobsPackageQueue, jobQueue);
		
		// hardcoded a initialized job list for testing
//		jobList.add(new JobObject("masterJobs/", "HelloWorld", "Melbourne", 1000, 0));
//		jobList.add(new JobObject("masterJobs/", "GoodDay", "Syndey", 2000, 1));
		
		/*JobsPackage jp = new JobsPackage();
		JobObject j = new JobObject();
		j.setExecuteFilePath("masterJobs/");
		j.setExecuteFileName("TestQueue");
		j.setParameters("Queue1");
		j.setTime(3000);
		j.setJobID(0);
		j.setClientID("CS0");
		j.setPriority(0);
		jp.addJob(j);
		j = new JobObject();
		j.setExecuteFilePath("masterJobs/");
		j.setExecuteFileName("TestQueue");
		j.setParameters("Queue2");
		j.setTime(3000);
		j.setJobID(1);
		j.setClientID("CS0");
		j.setPriority(0);
		jp.addJob(j);
		j = new JobObject();
		j.setExecuteFilePath("masterJobs/");
		j.setExecuteFileName("TestQueue");
		j.setParameters("Queue3");
		j.setTime(3000);
		j.setJobID(2);
		j.setClientID("CS0");
		j.setPriority(1);
		jp.addJob(j);
		jp.setBudget(30000);
		jp.setDeadline((long)30000);
		jp.setClientID("CS0");
		//jp.addJob(new JobObject("masterJobs/","HelloWorld", "Queue2", 4000, 4));
		//jp.addJob(new JobObject("masterJobs/","TestQueue", "Queue3", 5000, 5));
		
		//add jobpackage to queue
		masterService.queueJobsPackage(jp);
		masterService.jobPackageInfo.put(jp.getClientID(), jp);
		
		j = new JobObject();
		j.setExecuteFilePath("masterJobs/");
		j.setExecuteFileName("TestQueue");
		j.setParameters("Queue3");
		j.setTime(3000);
		j.setJobID(2);
		j.setClientID("CS1");
		j.setPriority(1);
		jp=new JobsPackage();
		jp.addJob(j);
		jp.setBudget(30000);
		jp.setDeadline((long)30000);
		jp.setClientID("CS1");
		
		//add jobpackage to queue
		masterService.queueJobsPackage(jp);
		masterService.jobPackageInfo.put(jp.getClientID(), jp);
		*/
		RegisterThread registerThread = new RegisterThread(masterService);
		WorkerTimeout workerTimeoutThread = new WorkerTimeout(masterService);
		//ReceiveJobPackagesThread receiveJobPackagesThread = new ReceiveJobPackagesThread(jobQueue, masterService);
		ClientRequestThread handleClient = new ClientRequestThread(masterService, masterTCPPort, jobsPackageQueue);
		AssignJobsThread assignJobsThread = new AssignJobsThread(masterService);
		ReceiveJobResultsThread receiveJobResultThread = new ReceiveJobResultsThread(masterService);

		registerThread.start();
		workerTimeoutThread.start();
		assignJobsThread.start();
		handleClient.start();
		//receiveJobPackagesThread.start();
		receiveJobResultThread.start();
		//MasterGUI masterGUI = new MasterGUI(masterService);
		//masterGUI.setVisible(true);
		//WorkerGUI workerGUI = new WorkerGUI(masterService);
		//workerGUI.setVisible(true);
		ServerInfoGUI s = new ServerInfoGUI(new JFrame(), new JFrame(), masterService);
		Thread t = new Thread(s);
		t.start();
		/******************** with Rigistry mode ********************/
	}
}

class RegisterThread extends Thread {

	MasterService masterService;

	public RegisterThread (MasterService masterService) {
		this.masterService = masterService;
	}

	public void run() {
		try {
			masterService.register(masterService);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

class WorkerTimeout extends Thread {

	MasterService masterService;

	public WorkerTimeout (MasterService masterService) {
		this.masterService = masterService;
	}

	public void run() {
		while (true) {
			try {
				//sleep for 3s in case Register information (1s) is delayed by network congestion
				Thread.sleep(3000);
				masterService.workerTimeout();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

//thread for assigning jobs from client to workers
class AssignJobsThread extends Thread {

	private MasterService masterService;

	public AssignJobsThread(MasterService masterService) {
		this.masterService = masterService;
	}

	public void run() {
		// TODO Auto-generated method stub
		
			try {
				// wait for the worker list
				sleep(10000);
				masterService.assignJobs();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
	}
}

//thread for requesting worker list from Cloud Server
class RequestThread extends Thread {

	private MasterService clientService;

	public RequestThread(MasterService clientService) {
		this.clientService = clientService;
	}

	public void run() {
		// TODO Auto-generated method stub
		try {
			clientService.requestList();		
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

class ReceiveJobResultsThread extends Thread {
	
	private MasterService masterService;
	
	public ReceiveJobResultsThread(MasterService masterService) {
		this.masterService = masterService;
	}
	
	public void run() {
		masterService.ReceiveJobResults(masterService);
	}
}